import serial
import struct
import time
from enum import Enum

# 串口配置
SERIAL_PORT = '/dev/ttyUSB0'  # 根据实际串口修改
BAUD_RATE = 115200

class CommandType(Enum):
    """命令类型枚举"""
    SENSOR_THRESHOLD = 0x55B1    # 传感器阈值设置
    MOTOR_CONTROL = 0x55B2       # 电机控制
    LED_CONTROL = 0x55B3         # LED控制
    SYSTEM_CONTROL = 0x55B4      # 系统控制
    QUERY_STATUS = 0x55B5        # 查询状态

def calculate_crc(data):
    """CRC16 MODBUS 校验算法"""
    crc = 0xFFFF
    for byte in data:
        crc ^= byte
        for _ in range(8):
            if crc & 0x01:
                crc = (crc >> 1) ^ 0xA001
            else:
                crc = crc >> 1
    return crc

def int32_to_big_endian(value):
    """将32位有符号整型转换为大端字节序"""
    return struct.pack('>i', value)

def uint32_to_big_endian(value):
    """将32位无符号整型转换为大端字节序"""
    return struct.pack('>I', value)

def float_to_big_endian(value):
    """将浮点型转换为大端字节序"""
    return struct.pack('>f', value)

def create_command_packet(command_type, fields_data):
    """创建命令数据包
    
    Args:
        command_type: CommandType枚举值
        fields_data: 字段数据列表，每个元素为 (type, data) 元组
    
    Returns:
        完整的命令数据包字节流
    """
    packet = bytearray()
    
    # 命令头部 (2字节) - 根据命令类型设置
    header = command_type.value
    packet.extend([(header >> 8) & 0xFF, header & 0xFF])
    
    # 总长度 (2字节) - 先占位，后面填写 (小端字节序)
    length_pos = len(packet)
    packet.extend([0x00, 0x00])
    
    # 字段数 (1字节)
    packet.append(len(fields_data))
    
    # 添加字段数据
    for field_type, field_data in fields_data:
        packet.append(field_type)
        
        if field_type == 0x01:  # 有符号整型
            data_bytes = int32_to_big_endian(field_data)
            packet.append(len(data_bytes))
            packet.extend(data_bytes)
            
        elif field_type == 0x02:  # 浮点型
            data_bytes = float_to_big_endian(field_data)
            packet.append(len(data_bytes))
            packet.extend(data_bytes)
            
        elif field_type == 0x03:  # 无符号整型
            data_bytes = uint32_to_big_endian(field_data)
            packet.append(len(data_bytes))
            packet.extend(data_bytes)
            
        elif field_type == 0x04:  # 字符串
            if isinstance(field_data, str):
                data_bytes = field_data.encode('utf-8')
            else:
                data_bytes = field_data
            packet.append(len(data_bytes))
            packet.extend(data_bytes)
            
        elif field_type == 0x05:  # 布尔型
            data_bytes = bytes([1 if field_data else 0])
            packet.append(len(data_bytes))
            packet.extend(data_bytes)
            
        else:
            raise ValueError(f"Unsupported field type: {field_type}")
    
    # 计算总长度（包括CRC 2字节）
    total_length = len(packet) + 2
    packet[length_pos] = total_length & 0xFF
    packet[length_pos + 1] = (total_length >> 8) & 0xFF
    
    # 计算CRC
    crc = calculate_crc(packet)
    packet.append(crc & 0xFF)
    packet.append((crc >> 8) & 0xFF)
    
    return bytes(packet)

def send_sensor_threshold_command(ser, temp_min, temp_max, humid_min, humid_max):
    """发送传感器阈值设置命令"""
    print(f"\n📊 发送传感器阈值设置命令:")
    print(f"   温度范围: {temp_min}°C ~ {temp_max}°C")
    print(f"   湿度范围: {humid_min}% ~ {humid_max}%")
    
    fields = [
        (0x02, temp_min),   # 温度最小值(浮点型)
        (0x02, temp_max),   # 温度最大值(浮点型)
        (0x02, humid_min),  # 湿度最小值(浮点型)
        (0x02, humid_max),  # 湿度最大值(浮点型)
    ]
    
    packet = create_command_packet(CommandType.SENSOR_THRESHOLD, fields)
    ser.write(packet)
    print(f"✅ 命令已发送: {packet.hex()}")

def send_motor_control_command(ser, speed, direction, enabled):
    """发送电机控制命令"""
    direction_str = {0: "停止", 1: "正转", 2: "反转"}.get(direction, "未知")
    print(f"\n🔧 发送电机控制命令:")
    print(f"   速度: {speed} RPM")
    print(f"   方向: {direction_str}")
    print(f"   使能: {'启用' if enabled else '禁用'}")
    
    fields = [
        (0x01, speed),      # 速度(有符号整型)
        (0x05, direction),  # 方向(布尔型：0=停止,1=正转,2=反转)
        (0x05, enabled),    # 使能(布尔型)
    ]
    
    packet = create_command_packet(CommandType.MOTOR_CONTROL, fields)
    ser.write(packet)
    print(f"✅ 命令已发送: {packet.hex()}")

def send_led_control_command(ser, red, green, blue, mode):
    """发送LED控制命令"""
    mode_str = {0: "关闭", 1: "常亮", 2: "闪烁"}.get(mode, "未知")
    print(f"\n💡 发送LED控制命令:")
    print(f"   颜色: RGB({red}, {green}, {blue})")
    print(f"   模式: {mode_str}")
    
    fields = [
        (0x03, red),    # 红色(无符号整型)
        (0x03, green),  # 绿色(无符号整型)
        (0x03, blue),   # 蓝色(无符号整型)
        (0x05, mode),   # 模式(布尔型)
    ]
    
    packet = create_command_packet(CommandType.LED_CONTROL, fields)
    ser.write(packet)
    print(f"✅ 命令已发送: {packet.hex()}")

def send_system_control_command(ser, command_id, parameter=0):
    """发送系统控制命令"""
    commands = {
        1: "重启系统",
        2: "停止数据发送", 
        3: "恢复数据发送",
        4: "重置所有设备",
        5: "进入节能模式"
    }
    
    print(f"\n⚙️ 发送系统控制命令:")
    print(f"   命令: {commands.get(command_id, '未知命令')}")
    print(f"   参数: {parameter}")
    
    fields = [
        (0x05, command_id),  # 命令ID(布尔型)
        (0x03, parameter),   # 参数(无符号整型)
    ]
    
    packet = create_command_packet(CommandType.SYSTEM_CONTROL, fields)
    ser.write(packet)
    print(f"✅ 命令已发送: {packet.hex()}")

def send_query_command(ser, device_type):
    """发送查询状态命令"""
    devices = {
        1: "温湿度传感器",
        2: "电机控制器",
        3: "LED控制器", 
        4: "按键状态",
        5: "系统状态",
        0: "所有设备"
    }
    
    print(f"\n❓ 发送查询命令:")
    print(f"   查询设备: {devices.get(device_type, '未知设备')}")
    
    fields = [
        (0x05, device_type),  # 设备类型(布尔型)
    ]
    
    packet = create_command_packet(CommandType.QUERY_STATUS, fields)
    ser.write(packet)
    print(f"✅ 命令已发送: {packet.hex()}")

def show_menu():
    """显示菜单"""
    print("\n" + "="*60)
    print("🎮 STM32 命令控制中心")
    print("="*60)
    print("1. 📊 设置传感器阈值")
    print("2. 🔧 控制电机")
    print("3. 💡 控制LED")
    print("4. ⚙️  系统控制")
    print("5. ❓ 查询设备状态")
    print("6. 🚀 快速测试(自动发送所有命令)")
    print("0. 🚪 退出程序")
    print("-"*60)

def get_float_input(prompt, default=None):
    """获取浮点数输入"""
    while True:
        try:
            value = input(f"{prompt}" + (f" (默认: {default})" if default else "") + ": ")
            if value == "" and default is not None:
                return default
            return float(value)
        except ValueError:
            print("❌ 请输入有效的数字!")

def get_int_input(prompt, min_val=None, max_val=None, default=None):
    """获取整数输入"""
    while True:
        try:
            value = input(f"{prompt}" + (f" (默认: {default})" if default else "") + ": ")
            if value == "" and default is not None:
                return default
            val = int(value)
            if min_val is not None and val < min_val:
                print(f"❌ 值不能小于 {min_val}!")
                continue
            if max_val is not None and val > max_val:
                print(f"❌ 值不能大于 {max_val}!")
                continue
            return val
        except ValueError:
            print("❌ 请输入有效的整数!")

def sensor_threshold_menu(ser):
    """传感器阈值设置菜单"""
    print("\n📊 传感器阈值设置")
    print("-"*30)
    temp_min = get_float_input("温度最小阈值(°C)", 15.0)
    temp_max = get_float_input("温度最大阈值(°C)", 35.0)
    humid_min = get_float_input("湿度最小阈值(%)", 30.0)
    humid_max = get_float_input("湿度最大阈值(%)", 80.0)
    
    send_sensor_threshold_command(ser, temp_min, temp_max, humid_min, humid_max)

def motor_control_menu(ser):
    """电机控制菜单"""
    print("\n🔧 电机控制")
    print("-"*20)
    speed = get_int_input("电机速度(RPM)", -3000, 3000, 1500)
    print("方向选择: 0=停止, 1=正转, 2=反转")
    direction = get_int_input("方向", 0, 2, 1)
    enabled = get_int_input("使能(0=禁用, 1=启用)", 0, 1, 1)
    
    send_motor_control_command(ser, speed, direction, bool(enabled))

def led_control_menu(ser):
    """LED控制菜单"""
    print("\n💡 LED控制")
    print("-"*15)
    red = get_int_input("红色亮度", 0, 255, 255)
    green = get_int_input("绿色亮度", 0, 255, 128)
    blue = get_int_input("蓝色亮度", 0, 255, 0)
    print("模式选择: 0=关闭, 1=常亮, 2=闪烁")
    mode = get_int_input("模式", 0, 2, 1)
    
    send_led_control_command(ser, red, green, blue, mode)

def system_control_menu(ser):
    """系统控制菜单"""
    print("\n⚙️ 系统控制")
    print("-"*15)
    print("1. 重启系统")
    print("2. 停止数据发送")
    print("3. 恢复数据发送")
    print("4. 重置所有设备")
    print("5. 进入节能模式")
    
    cmd_id = get_int_input("选择命令", 1, 5, 1)
    parameter = get_int_input("参数", 0, 65535, 0)
    
    send_system_control_command(ser, cmd_id, parameter)

def query_menu(ser):
    """查询菜单"""
    print("\n❓ 查询设备状态")
    print("-"*20)
    print("0. 所有设备")
    print("1. 温湿度传感器")
    print("2. 电机控制器")
    print("3. LED控制器")
    print("4. 按键状态")
    print("5. 系统状态")
    
    device_type = get_int_input("选择设备", 0, 5, 0)
    send_query_command(ser, device_type)

def auto_test(ser):
    """自动测试所有命令"""
    print("\n🚀 自动测试模式")
    print("正在发送所有类型的命令...")
    
    # 1. 设置传感器阈值
    send_sensor_threshold_command(ser, 18.0, 28.0, 40.0, 70.0)
    time.sleep(1)
    
    # 2. 控制电机
    send_motor_control_command(ser, 2000, 1, True)
    time.sleep(1)
    
    # 3. 控制LED
    send_led_control_command(ser, 255, 0, 255, 2)  # 紫色闪烁
    time.sleep(1)
    
    # 4. 系统控制
    send_system_control_command(ser, 3, 1)  # 恢复数据发送
    time.sleep(1)
    
    # 5. 查询所有设备
    send_query_command(ser, 0)
    
    print("✅ 自动测试完成!")

def main():
    """主函数"""
    print("🎮 STM32 命令控制中心启动")
    print(f"串口: {SERIAL_PORT}")
    print(f"波特率: {BAUD_RATE}")
    
    try:
        ser = serial.Serial(SERIAL_PORT, BAUD_RATE, timeout=1)
        print(f"✅ 串口 {ser.name} 已打开")
        
        while True:
            show_menu()
            choice = input("请选择操作 (0-6): ").strip()
            
            if choice == '0':
                print("👋 再见!")
                break
            elif choice == '1':
                sensor_threshold_menu(ser)
            elif choice == '2':
                motor_control_menu(ser)
            elif choice == '3':
                led_control_menu(ser)
            elif choice == '4':
                system_control_menu(ser)
            elif choice == '5':
                query_menu(ser)
            elif choice == '6':
                auto_test(ser)
            else:
                print("❌ 无效选择，请重试!")
            
            input("\n按回车键继续...")
                
    except serial.SerialException as e:
        print(f"❌ 串口错误: {e}")
        print("请检查串口连接和配置")
    except KeyboardInterrupt:
        print("\n👋 程序被用户中断")
    except Exception as e:
        print(f"❌ 程序异常: {e}")
    finally:
        try:
            ser.close()
            print("🔌 串口已关闭")
        except:
            pass

if __name__ == "__main__":
    main() 